/*******************************************************************************
 * Copyright (c) 2011 TXT e-solutions SpA
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * This work was performed within the IoT_at_Work Project
 * and partially funded by the European Commission's
 * 7th Framework Programme under the research area ICT-2009.1.3
 * Internet of Things and enterprise environments.
 *
 *
 * Authors:
 *     Cristoforo Seccia (TXT e-solutions SpA)
 *
 * Contributors:
 *      Domenico Rotondi (TXT e-solutions SpA)
 *******************************************************************************/
package it.txt.access.capability.demo.client.view;

import it.txt.access.capability.commons.schema.validation.CapabilitySchemaValidationHandler;
import it.txt.access.capability.commons.schema.validation.CapabilitySchemaValidationHandlerException;
import it.txt.access.capability.commons.signer.X509DocumentSigner;
import it.txt.access.capability.commons.signer.model.X509CertificateKeyValues;
import it.txt.access.capability.commons.signer.model.X509DocumentSignerInfo;
import it.txt.access.capability.demo.client.tcp.RequestClientSupport;
import it.txt.access.capability.commons.utils.MessageUtils;
import it.txt.access.capability.factory.CapabilitySchemaFactoryException;
import it.txt.access.capability.demo.client.tcp.RequestClient;
import it.txt.access.capability.demo.client.view.AccessRightsViewerDialog;
import it.txt.access.capability.demo.client.view.ClientGUI;
import it.txt.access.capability.demo.client.view.model.ServiceRequestCapabilityModel;
import it.txt.access.capability.demo.client.view.model.ServerConnectionParamsModel;
import it.txt.access.capability.demo.client.view.model.ServerResponseInfoModel;
import it.txt.access.capability.demo.schema.CapabilityRequestType;
import it.txt.access.capability.demo.schema.request.factory.CapabilityDemoRequestFactory;
import it.txt.access.capability.factory.CapabilitySchemaFactory;
import it.txt.access.capability.schema.AccessRightType;
import it.txt.access.capability.schema.AccessRightsCapabilityType;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.border.SoftBevelBorder;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 *
 * @author Cristoforo Seccia (TXT e-solutions SpA)
 */
public class ClientGUIController implements WindowListener, PropertyChangeListener {

    private static final Logger LOGGER = Logger.getLogger(ClientGUIController.class.getName());
    private static final Cursor DEFAULT_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
    private static final Cursor BUSY_CURSOR = new Cursor(Cursor.WAIT_CURSOR);
    private ServerConnectionParamsModel connectionParamsModel;
    private X509CertificateKeyValues certificateKeyValues;
    private AccessRightsCapabilityType capability;
    private RequestClient capabilityRequestClient;
    private File currentCapabilityFile;
    private final ClientGUI clientGUI;

    public ClientGUIController() {
        clientGUI = new ClientGUI(this);
    }

    public void createAndShowClient() {
        clientGUI.setDefaultCloseOperation(ClientGUI.DO_NOTHING_ON_CLOSE);
        clientGUI.addWindowListener(this);
        clientGUI.setVisible(true);
    }

    protected void setUserKeystoreData(X509DocumentSignerInfo model) {
        clientGUI.setCursor(BUSY_CURSOR);
        String title = "User Keystore data";
        String message = "Using User ID: " + model.getSignerID();
        try {
            LOGGER.log(Level.INFO, message);
            certificateKeyValues = X509DocumentSigner.getCertificateKeyValues(model);
            clientGUI.appendLogMessage(MessageUtils.getInfoMessage(title, message));
            clientGUI.appendLogMessage(MessageUtils.getInfoMessage(title,
                    "certificater Alias: " + certificateKeyValues.getAlias()));
            showInfoMessage(clientGUI, title, message);
        } catch (GeneralSecurityException ex) {
            message = ex.getMessage();
            LOGGER.log(Level.SEVERE, message, ex);
            clientGUI.appendLogMessage(MessageUtils.getErrorMessage(title, message));
            showErrorMessage(clientGUI, title, message);
        }
        clientGUI.setCursor(DEFAULT_CURSOR);
    }

    protected void setServerConnectionParams(ServerConnectionParamsModel model) {
        String title = "Server Connection Parameters";
        if (model.getServerTCPPort() < 0) {
            showErrorMessage(clientGUI, title, "Server Port must be greater than 0.");
            return;
        }
        if (model.getServerTCPPort() > 0xFFFF) {
            showErrorMessage(clientGUI, title, "Server Port out of range.");
            return;
        }
        if (model.getServerAddressIP().isEmpty()) {
            showErrorMessage(clientGUI, title, "Invalid Server Host/Addreess.");
            return;
        }
        connectionParamsModel = model;
        showInfoMessage(clientGUI, title, "Successfully set connection values.");
    }

    protected ServiceRequestCapabilityModel setUserCapability(File userCapabilityFile) {
        clientGUI.setCursor(BUSY_CURSOR);
        String title = "Service Request Capability";
        ServiceRequestCapabilityModel result = null;
        try {
            capability =
                    CapabilitySchemaFactory.getInstance().createAccessRights(userCapabilityFile);
            result = new ServiceRequestCapabilityModel();
            String policyResourceID = capability.getResourceID();
            result.setResourceID(policyResourceID);
            String capabilityIDString = capability.getAccessRightsCapabilityID();
            result.setCapabilityID(capabilityIDString);
            List<AccessRightType> arts = capability.getAccessRights().getAccessRight();
            List<String> operatrions = new ArrayList<String>();
            for (AccessRightType accessRightType : arts) {
                operatrions.add(accessRightType.getPermittedAction().getValue());
            }
            result.setOperations(operatrions);
            result.setCapabilityPath(userCapabilityFile.getAbsolutePath());
            currentCapabilityFile = userCapabilityFile;
            clientGUI.appendLogMessage(MessageUtils.getInfoMessage(title,
                    "Using Capability - ID: " + result.getCapabilityID()));
        }
        catch (CapabilitySchemaFactoryException ex) {
            LOGGER.log(Level.SEVERE, ex.getMessage(), ex.getMessage());
            showErrorMessage(clientGUI, title, ex.getMessage());
            clientGUI.appendLogMessage(MessageUtils.getErrorMessage(title, ex.getMessage()));
            resetServiceProgressState();
        } catch (CapabilitySchemaValidationHandlerException ex) {
            LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
            showErrorMessage(clientGUI, title, ex.getMessage());
            String errors = CapabilitySchemaValidationHandler.dumpValidationEvents(ex.getValidationEvents());
            clientGUI.appendLogMessage(MessageUtils.getErrorMessage(title, errors));
            resetServiceProgressState();
        }
        clientGUI.setCursor(DEFAULT_CURSOR);
        return result;
    }

    protected void viewUserCapability() {
        try {
            URI acceRightsUri = currentCapabilityFile.toURI();
            AccessRightsViewerDialog capabilityViewerDialog =
                    new AccessRightsViewerDialog(acceRightsUri, clientGUI, true);
            capabilityViewerDialog.setLocationRelativeTo(clientGUI);
            capabilityViewerDialog.setVisible(true);
        } catch (IOException ex) {
            LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
        } catch (SAXException ex) {
            LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
        }
    }

    protected void sendUserCapabilityRequest(String resourceID, String operation) {
        //reset the gui monitor state
        resetServiceProgressState();
        //The signed request to send
        String title = "Service Request";
        if (connectionParamsModel == null) {
            showErrorMessage(clientGUI, title, "Please set connection parameters.");
            return;
        }
        if (certificateKeyValues == null) {
            showErrorMessage(clientGUI, title, "Please set keystore data.");
            return;
        }
        if (capability == null) {
            showErrorMessage(clientGUI, title, "Please set your capability");
            return;
        }
        try {
            CapabilityRequestType requestType = CapabilityDemoRequestFactory.getInstance().createRequest(
                    operation, resourceID, capability);
            Document requestDoc =
                    CapabilityDemoRequestFactory.getInstance().createRequestDocument(requestType);
            X509DocumentSigner.signXMLElement(requestDoc.getDocumentElement(), certificateKeyValues,
                    CapabilityDemoRequestFactory.SIGN_DOCUMENT_AFTER_NODE);
            //set the signed riquest
            String message = "Starting at: " + new Date().toString();
            LOGGER.log(Level.INFO, message);
            clientGUI.appendLogMessage(MessageUtils.getInfoMessage(title, message));
            clientGUI.capabilityRequestInProgress();
            capabilityRequestClient = new RequestClient(connectionParamsModel, requestDoc, this);
            Thread capabilityRequestThread = new Thread(capabilityRequestClient);
            capabilityRequestThread.start();
        } 
        catch (Exception ex) {
            handleServiceRequestFinishedEvent();
            LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
            showErrorMessage(clientGUI, title, ex.getMessage());
            clientGUI.appendLogMessage(MessageUtils.getErrorMessage(title, ex.getMessage()));
        }
    }

    protected void stopCapabilityRequest() {
        if (capabilityRequestClient != null && capabilityRequestClient.isConnected()) {
            capabilityRequestClient.closeClientSocket();
            handleServiceRequestFinishedEvent();
        }
    }

    protected static void showErrorMessage(Component parent, String title, String msg) {
        JOptionPane.showMessageDialog(parent, msg, title, JOptionPane.ERROR_MESSAGE);
    }

    protected static void showInfoMessage(Component parent, String title, String msg) {
        JOptionPane.showMessageDialog(parent, msg, title, JOptionPane.INFORMATION_MESSAGE);
    }

    protected void resetServiceProgressState() {
        handleServiceProgressStateEvent(RequestClientSupport.STATE_SETTINGS,
                clientGUI.getProgressServiceResponseReceivedLabel());
        handleServiceProgressStateEvent(RequestClientSupport.STATE_SETTINGS,
                clientGUI.getProgressServiceRequestSentLabel());
        handleServiceProgressStateEvent(RequestClientSupport.STATE_SETTINGS,
                clientGUI.getProgressTCPConnectionLabel());
        handleServiceProgressStateEvent(RequestClientSupport.STATE_SETTINGS,
                clientGUI.getProgressRequestReadyLabel());
    }

    private void stopClientAndCloseApplication() {
        stopCapabilityRequest();
        clientGUI.setVisible(false);
        LOGGER.log(Level.WARNING, "Server return code (Finish: {0} - Cancel: {1} - Error: {2}): {3} ",
                new Object[]{ClientGUI.FINISH_RETURN_CODE, ClientGUI.CANCEL_RETURN_CODE,
                    ClientGUI.ERROR_RETURN_CODE, clientGUI.getReturnCode()});
        System.exit(0);
    }

    private void handleReadyToAcceptStateEvent(PropertyChangeEvent evt) {
        Integer newState = (Integer) evt.getNewValue();
        JLabel progressLabel = clientGUI.getProgressRequestReadyLabel();
        handleServiceProgressStateEvent(newState, progressLabel);
        clientGUI.changeStatusLabelText(evt.getPropertyName());
    }

    private void handleTCPConnectionStateEvent(PropertyChangeEvent evt) {
        Integer newState = (Integer) evt.getNewValue();
        JLabel progressLabel = clientGUI.getProgressTCPConnectionLabel();
        handleServiceProgressStateEvent(newState, progressLabel);
        clientGUI.changeStatusLabelText(evt.getPropertyName());
    }

    private void handleRequestSentStateEvent(PropertyChangeEvent evt) {
        Integer newState = (Integer) evt.getNewValue();
        JLabel progressLabel = clientGUI.getProgressServiceRequestSentLabel();
        handleServiceProgressStateEvent(newState, progressLabel);
        clientGUI.changeStatusLabelText(evt.getPropertyName());
    }

    private void handleResponseReceivedStateEvent(PropertyChangeEvent evt) {
        Integer newState = (Integer) evt.getNewValue();
        JLabel progresslabel = clientGUI.getProgressServiceResponseReceivedLabel();
        handleServiceProgressStateEvent(newState, progresslabel);
        clientGUI.changeStatusLabelText(evt.getPropertyName());
    }

    private void handleResponceReceivedInfoEvent(PropertyChangeEvent evt) {
        ServerResponseInfoModel infoModel = (ServerResponseInfoModel) evt.getNewValue();
        clientGUI.validateResponseInformation(infoModel);
        clientGUI.appendLogMessage(MessageUtils.getInfoMessage("Server Response ID: ", infoModel.getResponseID()));
        clientGUI.appendLogMessage(MessageUtils.getInfoMessage("Server response Status: ", infoModel.getServiceResponseStatus()));
    }

    private void handleServiceRequestFinishedEvent() {
        LOGGER.log(Level.INFO, "Capability Request finished.");
        clientGUI.appendLogMessage(MessageUtils.getInfoMessage("Capability Request Process",
                "Finished at: " + new Date().toString()));
        clientGUI.capabilityRequestFinished();
    }

    private void handleServiceProgressStateEvent(int newState, JLabel progressLabel) {
        Icon newIcon = null;
        String message = progressLabel.getText();
        switch (newState) {
            case RequestClientSupport.STATE_OK: {
                newIcon = ClientGUI.ICON_OK;
                message += " - [Ok].";
                progressLabel.setBorder(new SoftBevelBorder(SoftBevelBorder.RAISED));
                break;
            }
            case RequestClientSupport.STATE_ERROR: {
                newIcon = ClientGUI.ICON_ERROR;
                message += " - [Error].";
                progressLabel.setBorder(new SoftBevelBorder(SoftBevelBorder.RAISED));
                break;
            }
            case RequestClientSupport.STATE_SETTINGS: {
                newIcon = ClientGUI.ICON_SETTINGS;
                message += " - [Reset].";
                progressLabel.setBorder(new SoftBevelBorder(SoftBevelBorder.LOWERED));
                break;
            }
        }
        progressLabel.setIcon(newIcon);
        clientGUI.appendLogMessage(MessageUtils.getInfoMessage("Demo Client", message));
    }

    public void propertyChange(PropertyChangeEvent evt) {
        LOGGER.log(Level.INFO, "Handling Event: {0} with value:{1}", new Object[]{evt.getPropertyName(), evt.getNewValue()});
        if (RequestClientSupport.EVENT_NAME_SERVICE_REQUEST_READY.equals(evt.getPropertyName())) {
            handleReadyToAcceptStateEvent(evt);
        } else if (RequestClientSupport.EVENT_NAME_TCP_CONNECTION.equals(evt.getPropertyName())) {
            handleTCPConnectionStateEvent(evt);
        } else if (RequestClientSupport.EVENT_NAME_SERVICE_REQUEST_SENT.equals(evt.getPropertyName())) {
            handleRequestSentStateEvent(evt);
        } else if (RequestClientSupport.EVENT_NAME_SERVICE_RESPONSE_RECEIVED.equals(evt.getPropertyName())) {
            handleResponseReceivedStateEvent(evt);
        } else if (RequestClientSupport.EVENT_NAME_SERVICE_RESPONSE_INFO.equals(evt.getPropertyName())) {
            handleResponceReceivedInfoEvent(evt);
        } else if (RequestClientSupport.EVENT_NAME_SERVICE_REQUEST_FINISHED.equals(evt.getPropertyName())) {
            handleServiceRequestFinishedEvent();
        }
    }

    public void windowClosing(WindowEvent e) {

        if (clientGUI.getReturnCode() == ClientGUI.FINISH_RETURN_CODE) {
            stopClientAndCloseApplication();
        } else {
            int resp = JOptionPane.showConfirmDialog(clientGUI,
                    "Are you sure you want to close?");
            if (resp == JOptionPane.YES_OPTION) {
                stopClientAndCloseApplication();
            }
        }
    }

    public void windowOpened(WindowEvent e) {
    }

    public void windowClosed(WindowEvent e) {
    }

    public void windowIconified(WindowEvent e) {
    }

    public void windowDeiconified(WindowEvent e) {
    }

    public void windowActivated(WindowEvent e) {
    }

    public void windowDeactivated(WindowEvent e) {
    }
}
